package Utils.upload;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FileUpload implements FilenameFilter {
	String uploadDir;

	int maxSize;

	long counter;

	Vector listeners;

	int maxFilenameLength = 40;

	public FileUpload(String uploaddir, int maxsize) {
		uploadDir = uploaddir;
		File f = new File(uploaddir);
		f.mkdirs();
		maxSize = maxsize;
		calculateCounter();
		listeners = new Vector();
	}

	public String getUploadDir() {
		return uploadDir;
	}

	public void setUploadDir(String dir) {
		uploadDir = dir;
		calculateCounter();
	}

	//	public void addListener(FileUploadListener listener) {
	//		listeners.add(listener);
	//	}

	public void removeListener(FileUploadListener listener) {
		listeners.remove(listener);
	}

	void calculateCounter() {
		counter = 0;
		File root = new File(uploadDir);
		root.listFiles(this);
	}

	public boolean accept(File dir, String file) {
		File f = new File(dir, file);
		if (f.isDirectory())
			return false;
		int idx1 = file.indexOf("&npk");
		if (idx1 > 0 && file.charAt(0) == '[') {
			try {
				long v = Long.parseLong(file.substring(1, idx1));
				if (v > counter)
					counter = v;
			} catch (NumberFormatException e) {
			}
		}
		return false;
	}

	public Hashtable<String, Section> getUpload(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		return getUpload(req, res, true);
	}

	public Hashtable<String, Section> getUpload(HttpServletRequest req, HttpServletResponse res,
			boolean useFileName) throws ServletException, IOException {
		try {
			req.setCharacterEncoding("UTF-8");
			Hashtable<String, Section> items = new Hashtable<String, Section>();
			if (req == null)
				return null;
			String contentType = req.getContentType(); //req.getHeader("Content-type");
			if (contentType == null)
				return null;
			//			System.out.println("FileUpload: content type "+req.getContentType());
			if (!contentType.toLowerCase().startsWith("multipart/form-data")) {
				return null;
			}
			/*
			 if (maxSize > 0 && req.getContentLength() > maxSize) {
			 System.out.println("FileUpload: content too large, limit is "+maxSize);
			 return null;
			 }
			 */
			int i = contentType.indexOf("boundary=");
			if (i == -1) {
				System.out.println("FileUpload: could not locate boundry");
				return null;
			}
			String sep = contentType.substring(i + 9);
			if (sep == null) {
				System.out.println("FileUpload: unexpected end of data");
				return null;
			}
			sep = "--" + sep;
			ServletInputStream is = req.getInputStream();
			Section section;
			boolean first = true;
			while (true) {
				try {
					section = getNextSection(is, sep, first);
					first = false;
					if (section != null) {
						items.put(section.getName(), section);
					} else break;
				} catch (IOException e) {
//					break;
				}
			}
			is.close();
			Enumeration enum1 = listeners.elements();
			FileUploadListener listener;
			while (enum1.hasMoreElements()) {
				listener = (FileUploadListener) enum1.nextElement();
				listener.uploadComplete(items);
			}
			return items;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	String readLine(ServletInputStream is) throws IOException {
		byte b[] = new byte[8164];
		int len;
		String line;

		len = is.readLine(b, 0, b.length);
		if (len == -1)
			return null;
		if (b[len - 1] == '\n')
			len--;
		if (b[len - 1] == '\r')
			len--;
		line = new String(b, 0, len,"UTF-8");
		
		//		System.out.println("readLine "+line);
		return line;
	}

	Section getNextSection(ServletInputStream is, String sep, boolean first)
			throws IOException {
		byte b[] = new byte[32 * 1024]; //32k chunks
		byte b2[] = new byte[32 * 1024]; //32k chunks
		byte[] oldbuf, newbuf;
		int len, oldlen = 0;
		String line;
		Section section = null;
		if (first) {
			line = readLine(is);
			if (line == null)
				return null;

			if (!line.equals(sep)) { //no starting separator
				System.out.println("FileUpload: could not locate separator");
				return null;
			}
		}
		section = getSectionHeader(is);
		if (section == null)
			return null;
		if (section.isParameter()) {
			StringBuffer parmbuf = new StringBuffer();
			//			section.setValue(readLine(is));
			while (true) {
				line = readLine(is);
				if (!line.startsWith(sep)) {
					//					System.out.println("FileUpload: could not locate ending separator");
					parmbuf.append(line);
				} else
					break;
			}
			section.setValue(parmbuf.toString());
		} else {
			synchronized (this) {
				counter++;
			}
			//String newname = "[" + counter + "]" + section.getFileName();
			String newname = section.getFileName();
			if (newname.length() > maxFilenameLength) {
				int idx = newname.lastIndexOf(".");
				String ext = "";
				if (idx > -1) {
					ext = newname.substring(idx);
					newname = newname.substring(0, idx);
				}
				newname = newname
						.substring(0, maxFilenameLength - ext.length())
						+ ext;
			}
			//section.setFileName(newname);
			//			System.out.println("file "+newname);
			if (!(uploadDir.endsWith("/"))) {
				uploadDir += "/";
			}
			section.setSavedFileName(uploadDir + newname);
			FileOutputStream fos = null;
			byte[] endSection = (sep).getBytes();
			oldbuf = null;
			newbuf = b2;
			//			byte[] temp;
			long total = 0;
			try {
				fos = new FileOutputStream(section.getSavedFileName());
				while ((len = is.readLine(newbuf, 0, newbuf.length)) != -1) {
					if (matches(newbuf, len, endSection)) {
						break;
					}
					if (oldbuf != null) {
						if (maxSize == 0 || total < maxSize) {
							fos.write(oldbuf, 0, oldlen);
							total += oldlen;
						}
					}
					oldlen = len;
					if (newbuf == b2) {
						newbuf = b;
						oldbuf = b2;
					} else {
						newbuf = b2;
						oldbuf = b;
					}
				}
				if (oldbuf != null && oldlen > 2
						&& (maxSize == 0 || total < maxSize)) {
					fos.write(oldbuf, 0, oldlen - 2); //don't count added \r\n prior to boundry
					total += oldlen - 2;
				}
				fos.close();
				if (maxSize != 0 && total > maxSize) {
					File f = new File(section.getSavedFileName());
					f.delete();
				}
			} catch (IOException e) {
				if (fos != null) {
					fos.close();
					File f = new File(section.getSavedFileName());
					f.delete();
				}
				throw e;
			}
		}
		return section;
	}

	boolean matches(byte[] b, int len, byte[] sep) {
		if (len < sep.length + 2)
			return false;
		for (int i = 0; i < sep.length; i++) {
			if (b[i] != sep[i])
				return false;
		}
		return true;
	}

	Section getSectionHeader(ServletInputStream is) throws IOException {
		String line, s;
		String name = null, filename = null;

		line = readLine(is);
		if (line == null)
			return null;
		s = line.toLowerCase();
		if (!s.startsWith("content-disposition:")) {
			System.out.println("No content-disposition: " + s);
			return null;
		}

		int startidx = s.indexOf(";", 20);
		if (startidx == -1)
			return null;
		String disp = s.substring(20, startidx).trim().toLowerCase();
		if (!disp.equals("form-data")) {
			System.out.println("not form-data");
			return null;
		}
//		int l = s.length();
		String lcline = s.substring(startidx + 1);
		line = line.substring(startidx + 1);
		//		System.out.println("line:");
		//		System.out.println(line);
		int sidx = lcline.indexOf("name=");
		if (sidx == -1)
			return null;
		int eidx = lcline.indexOf(";", sidx + 5);
		if (eidx != -1)
			name = line.substring(5 + sidx, eidx).trim();
		else
			name = line.substring(5 + sidx).trim();
		if (name.startsWith("\""))
			name = name.substring(1);
		if (name.endsWith("\""))
			name = name.substring(0, name.length() - 1);
		sidx = lcline.indexOf("filename=");
		if (sidx != -1) {
			eidx = lcline.indexOf(";", sidx + 9);
			if (eidx != -1)
				filename = line.substring(9 + sidx, eidx).trim();
			else
				filename = line.substring(9 + sidx).trim();
			if (filename.startsWith("\""))
				filename = filename.substring(1);
			if (filename.endsWith("\""))
				filename = filename.substring(0, filename.length() - 1);
		}
		name = name.trim();
		if (filename != null) {
			filename = filename.trim();
			filename = removeStupidIEFullPath(filename);
			if (filename.equals(""))
				filename = null;
		}
		Section section = new Section(name, filename);
		while (true) {
			line = readLine(is);
			if (line == null)
				break;
			if (line.equals(""))
				break;
			s = line.toLowerCase().trim();
			if (s.startsWith("content-type:")) {
				section.setContentType(line.substring(13).trim());
			}
			//read everything up to the empty line, skipping stuff like Content-Type entries
		}
		//at this point we've read the stream up to the start of the data
		return section;
	}

	String removeStupidIEFullPath(String filename) {
		int idx = filename.lastIndexOf("/");
		if (idx != -1)
			filename = filename.substring(idx + 1);
		idx = filename.lastIndexOf("\\");
		if (idx != -1)
			filename = filename.substring(idx + 1);
		return filename;
	}
}
